package cn.iocoder.yudao.module.scenic.service.checkin;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.scenic.controller.admin.checkin.vo.CheckInPageByLocationReqVO;
import cn.iocoder.yudao.module.scenic.controller.admin.checkin.vo.CheckInPageByLocationResVO;
import cn.iocoder.yudao.module.scenic.controller.admin.checkin.vo.CheckInPageReqVO;
import cn.iocoder.yudao.module.scenic.controller.admin.checkin.vo.CheckInSaveReqVO;
import cn.iocoder.yudao.module.scenic.controller.admin.comment.vo.CommentPageByLocationResVO;
import cn.iocoder.yudao.module.scenic.controller.admin.spot.vo.SpotRespVO;
import cn.iocoder.yudao.module.scenic.controller.app.checkin.vo.AppCheckInSaveReqVO;
import cn.iocoder.yudao.module.scenic.controller.app.checkin.vo.CheckInAppPageReqVO;
import cn.iocoder.yudao.module.scenic.controller.app.checkin.vo.CheckInPageResVO;
import cn.iocoder.yudao.module.scenic.controller.app.collect.vo.SaveCollectPageResVO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.checkin.CheckInDO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.collect.CollectDO;
import cn.iocoder.yudao.module.scenic.dal.dataobject.spot.SpotDO;
import cn.iocoder.yudao.module.scenic.dal.mysql.checkin.CheckInMapper;
import cn.iocoder.yudao.module.scenic.dal.mysql.spot.SpotMapper;
import cn.iocoder.yudao.module.scenic.service.geo.GeoComponent;
import cn.iocoder.yudao.module.scenic.service.spot.SpotService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.Distance;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.scenic.enums.ErrorCodeConstants.CHECK_IN_NOT_EXISTS;

/**
 * 实践点打卡 Service 实现类
 *
 * @author 芋道源码
 */
@Slf4j
@Service
@Validated
public class CheckInServiceImpl implements CheckInService {

    public static final String CHECK_IN_GEO_KEY = "check_in_geo_key";

    @Resource
    private CheckInMapper checkInMapper;

    @Resource
    SpotService spotService;

    @Resource
    private GeoComponent geoComponent;

    @Resource
    DeptApi deptApi;

    @Resource
    SpotMapper spotMapper;


    @Override
    public Long createCheckIn(CheckInSaveReqVO createReqVO) {
        // 插入
        CheckInDO checkIn = BeanUtils.toBean(createReqVO, CheckInDO.class);
        checkInMapper.insert(checkIn);
        // 返回
        return checkIn.getId();
    }

    @Override
    public void updateCheckIn(CheckInSaveReqVO updateReqVO) {
        // 校验存在
        validateCheckInExists(updateReqVO.getId());
        // 更新
        CheckInDO updateObj = BeanUtils.toBean(updateReqVO, CheckInDO.class);
        checkInMapper.updateById(updateObj);
    }

    @Override
    public void deleteCheckIn(Long id) {
        // 校验存在
        validateCheckInExists(id);
        // 删除
        checkInMapper.deleteById(id);
    }

    private void validateCheckInExists(Long id) {
        if (checkInMapper.selectById(id) == null) {
            throw exception(CHECK_IN_NOT_EXISTS);
        }
    }

    @Override
    public CheckInDO getCheckIn(Long id) {
        return checkInMapper.selectById(id);
    }

    @Override
    public PageResult<CheckInDO> getCheckInPage(CheckInPageReqVO pageReqVO) {
        checkInMapper.selectPage(pageReqVO);
        return checkInMapper.selectPage(pageReqVO);
    }

    @Override
    public CommonResult checkIn(AppCheckInSaveReqVO createReqVO) {
        Long spotId = createReqVO.getSpotId();
        SpotDO spot = spotService.getSpot(spotId);
        //获取是实践点
        String key = CHECK_IN_GEO_KEY+":"+IdWorker.getId();
        geoComponent.geoAdd(key, Double.parseDouble(spot.getLnt()), Double.parseDouble(spot.getLat()), spot.getName());
        geoComponent.geoAdd(key,Double.parseDouble(createReqVO.getLnt()), Double.parseDouble(createReqVO.getLat()), createReqVO.getNickName());
        Distance distance = geoComponent.geoDist(key, spot.getName(), createReqVO.getNickName());
        double distanceInKm = distance.getValue() / 1000;
        log.info("实际打卡距离为：{}",distanceInKm);
        //如果距离超过500米，则打卡失败
        if(distanceInKm > 500){
            return CommonResult.error(500,"距离实践点距离太远，打卡失败");
        }
        // 插入
        CheckInDO checkIn = BeanUtils.toBean(createReqVO, CheckInDO.class);
        checkInMapper.insert(checkIn);
        // 返回
        return CommonResult.success(checkIn.getId());
    }

    @Override
    public PageResult<CheckInPageByLocationResVO> checkInPageByLocation(CheckInPageByLocationReqVO pageReqVO) {
        IPage<CheckInPageByLocationResVO> allPage = new Page(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        IPage<CheckInPageByLocationResVO> checkInList = checkInMapper.selectCheckInListByCondition(allPage,pageReqVO);
        PageResult<CheckInPageByLocationResVO> page = new PageResult<>();
        if( 0L != checkInList.getTotal()) {
            Set<Long> idSet = new HashSet<>();
            for (CheckInPageByLocationResVO spotDO : checkInList.getRecords()) {
                idSet.add(spotDO.getProvinceId());
                idSet.add(spotDO.getCityId());
                idSet.add(spotDO.getDistrictId());
            }
            List<Long> idList = new ArrayList<>(idSet);
            CommonResult<List<DeptRespDTO>> result = deptApi.getDeptList(idList);
            Map<Long, DeptRespDTO> deptRespDTOMap = new HashMap<>();
            if (result.isSuccess()) {
                List<DeptRespDTO> deptRespDTOList = result.getData();
                if (!CollectionUtils.isEmpty(deptRespDTOList)) {
                    deptRespDTOMap = deptRespDTOList.stream().collect(Collectors.toMap(DeptRespDTO::getId, Function.identity()));
                }
            }
            Map<Long, DeptRespDTO> finalDeptRespDTOMap = deptRespDTOMap;
            List<CheckInPageByLocationResVO> spotRespVOList = checkInList.getRecords().stream().map(s -> {
                Long provinceId = s.getProvinceId();
                Long cityId = s.getCityId();
                Long districtId = s.getDistrictId();
                StringBuilder belongAddress = new StringBuilder();
                if (finalDeptRespDTOMap.containsKey(provinceId)) {
                    belongAddress.append(finalDeptRespDTOMap.get(provinceId).getName());
                    belongAddress.append("-");
                }
                if (finalDeptRespDTOMap.containsKey(cityId)) {
                    belongAddress.append(finalDeptRespDTOMap.get(cityId).getName());
                    belongAddress.append("-");
                }
                if (finalDeptRespDTOMap.containsKey(districtId)) {
                    belongAddress.append(finalDeptRespDTOMap.get(districtId).getName());
                }
                s.setBelongAddress(belongAddress.toString());
                return s;
            }).collect(Collectors.toList());
            page.setList(spotRespVOList);
        }
        page.setTotal(checkInList.getTotal());

        return page;
    }

    @Override
    public PageResult<CheckInPageResVO> page(CheckInAppPageReqVO reqVO) {
        PageResult <CheckInPageResVO> checkInPageResVOPageResult = new PageResult<>();
        IPage<CheckInDO> allPage = new Page(reqVO.getPageNo(), reqVO.getPageSize());
        QueryWrapper<CheckInDO> wrapper =new QueryWrapper();
        wrapper.eq("member_id",reqVO.getOpenId());
        wrapper.orderByDesc("create_time");
        wrapper.eq("deleted",false);
        List<CheckInDO> checkInDOList = checkInMapper.selectList(allPage, wrapper);
        if(!CollectionUtil.isEmpty(checkInDOList)) {
            List<Long> collect = checkInDOList.stream().map(CheckInDO::getSpotId).distinct().collect(Collectors.toList());
            QueryWrapper<SpotDO> spotDOQueryWrapper = new QueryWrapper();
            spotDOQueryWrapper.in("id", collect);
            List<SpotDO> spotDOS = spotMapper.selectList(spotDOQueryWrapper);
            Map<Long, SpotDO> collect1 = spotDOS.stream().collect(Collectors.toMap(SpotDO::getId, Function.identity()));
            List<CheckInPageResVO> collect2 = checkInDOList.stream().map(s -> {
                CheckInPageResVO vo = new CheckInPageResVO();
                Long spotId = s.getSpotId();
                vo.setSpotId(spotId);
                vo.setCreateTime(s.getCreateTime());
                if (collect1.containsKey(spotId)) {
                    SpotDO spotDO = collect1.get(spotId);
                    vo.setName(spotDO.getName());
                    vo.setPhotoUrl(spotDO.getPhotoUrl());
                }
                return vo;
            }).collect(Collectors.toList());
            checkInPageResVOPageResult.setList(collect2);
        }
        checkInPageResVOPageResult.setTotal(allPage.getTotal());
        return checkInPageResVOPageResult;
    }

}